﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;

namespace ClockSync2
{
    class Slave
    {
        private RunningClock clock;
        private Neighbour master;
        private tcpServer ser;
        private tcpClient cli;
        private Neighbour self;
        int offsetsum;
        int offsetCounter;
        System.Threading.Thread clockThread;
        
        public Slave(NeighbourTable ntb, RunningClock clock)
        {
            offsetsum = 0;
            offsetCounter = 0;
            this.clock = clock;           
            this.master = ntb.getMaster();
            int myPortOffset = 0;
            
            //get myself
            foreach (Neighbour ne in ntb.Neighbours)
            {
                if (ne.Address.Equals(utils.FindLanAddress()))
                {
                    self = ne;
                }
            }

            
            myPortOffset = self.syncPort;

            //an error hapend and i did not find my self
            if (myPortOffset == 0)
            {
                throw new ArgumentNullException();
            }
           
            int attempt = 1;

            Console.WriteLine(self.Address + " connectint to server " + master.Address);
           
            //attempting to connect
            while (true)
            {
                try//to connect
                {
                    Console.WriteLine("\t"+self.Address + " is attempting to connect to server");
                    //creat server for getting messages from master
                    ser = new tcpServer(IPAddress.Any, myPortOffset + ntb.Neighbours.Count);
                    Console.WriteLine("\tconnected");
                    break;
                }
                catch//faild to connect
                {
                    Console.WriteLine("\t" + self.Address + " faild to connent to the server");
                    if (attempt < 3)//try again, max 3 attemptes
                    {
                        attempt++;
                    }
                    else//attempted to connect 3 times and faild
                    {
                        Console.WriteLine("\t" + self.Address + " failed to connect to the server, exiting");
                       throw new ElectionStartedException(self.Address.ToString(),self);
                    }

                }
            }
           
        }

        public void start()
        {
            byte[] offset=null;

            //tkura
            DateTime start=DateTime.Now;
            DateTime stop=DateTime.Now;

            //get message from master, expected !!! - start sync

            string msg = null;
            try
            {
                msg = utils.ByteToStrArray(ser.getMsg());
            }
            catch
            {
                Console.WriteLine("\tgetting message faild, exiting");
               throw new ElectionStartedException(self.Address.ToString(),self);
            }
            

            int attempt = 1;
            Console.WriteLine(self.Address + " opening client");

            //open clinet for sending messages to master, ther are three attemptes
            while (true)
            {
                try
                {
                    Console.WriteLine("\t" + self.Address + " is attempting to creat client");
                    cli = new tcpClient(master.Address, self.syncPort);
                    Console.WriteLine("\t connected");
                    break;
                }
                catch//faild to creat client
                {
                    Console.WriteLine("\t" + self.Address + " faild to connent");
                    if (attempt < 3)//try again
                    {
                        attempt++;
                    }
                    else//faild three times exiting...
                    {
                        Console.WriteLine("\t" + self.Address + " failed to connect, exiting");
                       throw new ElectionStartedException(self.Address.ToString(),self);
                    }

                }
            }

            //sync in loop

            while (true)
            {
                Console.WriteLine("in slave.start():: \n\t(start sync) slave recived msg: \"" + msg + "\"");
               

                if (msg.Equals("!!!"))//if i got sync message
                {
                    try//try to sean the master my time
                    {
                        cli.sendMsg(clock.getTime());
                        //stop = DateTime.Now;
                        //Console.WriteLine("*&*&*&*&*&*&*&*&*&*");
                        //Console.WriteLine("TKURA OF SLAVE=\t"+(int)utils.subDateTimesGetTotMS(stop,start));
                        //Console.WriteLine("*&*&*&*&*&*&*&*&*&*");
                    }
                    catch//faild to sean to master my time
                    {
                        Console.WriteLine("\tgetting message faild, exiting");
                       throw new ElectionStartedException(self.Address.ToString(),self);
                    }
                    Console.WriteLine("\tslave sent msg (my time):\" " + clock.getTime() + "\"");
                }

                try
                {
                    offset = ser.getMsg();
                }
                catch
                {
                    Console.WriteLine("\tgetting message faild, exiting");
                   throw new ElectionStartedException(self.Address.ToString(),self);
                }

                string srtOffset = utils.ByteToStrArray(offset);

                Console.WriteLine("\tstr offset: " + srtOffset);
                int intOffset = 0;
                offsetsum += Math.Abs(intOffset);
                offsetCounter++;
                Console.WriteLine("offset avr = " + (offsetsum / offsetCounter));
                try
                {
                    intOffset = int.Parse(srtOffset);
                }
                catch
                {
                    Console.WriteLine("\tparsint offset faild, exiting");                    
                   throw new ElectionStartedException(self.Address.ToString(),self);
                }
                clock.setTime(intOffset);
                
                Console.WriteLine("\tslave's time after adding offet is:" + clock.getTime());

                try
                {
                    msg = utils.ByteToStrArray(ser.getMsg());
                }
                catch
                {
                    Console.WriteLine("\tgetting message faild, exiting");
                    throw new ElectionStartedException(self.Address.ToString(),self);
                }
                //start = DateTime.Now;
            }
        }
    }
}
